/**
 * \file Symbol.cpp
 * \brief Implementation of SymbolTable and Symbol subclasses
 * \author Wouter Caarls <w.caarls@tnw.tudelft.nl> 
 *
 * \verbatim
 * Copyright (c) 2005 Wouter Caarls, Delft University of Technology
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * \endverbatim
 */

#include "Code.h"
#include "Expression.h"
#include "Symbol.h"

Expression* Variable::reduce(SymbolTable *symbols, Compound *out, Use use)
{
  LOG(plog.debug, HDR << "variable " << identifier << " " << use << std::endl);

  if (data->known)
  {
    return data->reduce(symbols, out, use);
  }
  else
    return new Id(data->type, identifier);
}

std::string Variable::toString(int indentation)
{
  CStr res;

  res << indent(indentation) << data->type->toString() << " "
      << identifier << " = " << data->toString();

  bool outputTentative = g_outputTentative;
  g_outputTentative = true;

  if (!data->assignments.empty())
  {
    res << "{ ";
    for (Value::AI ii=data->assignments.begin(); ii != data->assignments.end(); ++ii)
      res << (*ii)->toString() << " ";
    res << "} ";
  }
  
  if (!data->declarations.empty())
  {
    res << "{ ";
    for (Value::DI ii=data->declarations.begin(); ii != data->declarations.end(); ++ii)
      res << (*ii)->toString() << " ";
    res << "}";
  }

  g_outputTentative = outputTentative;
  
  return (char*)res;
}

/* ************************************************************************* */

Code* Function::reduce(SymbolTable *symbols, Compound *out, Use use)
{
  return data->copy();
}

std::string Function::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << data->typespec->type->toString() << " " << identifier << " = "
		 << "{ ... }");
}

/* ************************************************************************* */

Code* Strategy::reduce(SymbolTable *symbols, Compound *out, Use use)
{
  return data->copy();
}

std::string Strategy::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << "strategy " << identifier << " = "
		 << "{ ... }");
}

/* ************************************************************************* */

Code* TypeSymbol::reduce(SymbolTable *symbols, Compound *out, Use use)
{
  return data->makeValue();
}

std::string TypeSymbol::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << "typedef " << identifier << " = "
		 << data->toString());
}

/* ************************************************************************* */

void SymbolTable::newFrame()
{
  addSymbol(new Scope());
}

void SymbolTable::newFrame(std::string identifier)
{
  addSymbol(new Scope((char*)(CStr("__scope__") << identifier)));
}

void SymbolTable::popFrame()
{
  int i=1;
  TSymbol *back;

  if (table.empty())
    return;

  while (typeid(*(back = table.back())) != typeid(Scope))
  {
    LOG(plog.symbols, HDR << "popping " << table.back()->toString() << std::endl);

    if (back->owner == this && typeid(*back) != typeid(TypeSymbol))
      delete back;
    table.pop_back();
    i++;
  }

  if (back->owner == this)
    delete back;
  table.pop_back();
}

Scope *SymbolTable::getScope(std::string identifier)
{
  return dynamic_cast<Scope*>(findSymbol((char*)(CStr("__scope__") << identifier)));
}

void SymbolTable::addSymbol(TSymbol *symbol, bool addToFront, bool copyOnWrite)
{
  if (addToFront)
  {
    start++;
    symbol->index = -start;
    table.push_front(symbol);
  }
  else
  {
    symbol->index = -start+table.size();
    table.push_back(symbol);
  }

  if (!copyOnWrite)
  {
    symbol->owner = this;
    LOG(plog.symbols, HDR << symbol->toString() << " at index " << symbol->index << std::endl);
  }
}

TSymbol *SymbolTable::findSymbol(std::string identifier)
{
  if (table.empty())
    return NULL;

  for (RTI ii=table.rbegin(); ii != table.rend(); ++ii)
    if ((*ii)->identifier == identifier)
    {
      //std::cout << "SymbolTable::findSymbol " << (*ii)->toString() << std::endl;

      if ((*ii)->owner != this)
      {
        LOG(plog.crawl, HDR << this << " is not owner of symbol " << (*ii) << ", reposessing" << std::endl);
        TSymbol *symcopy = (*ii)->copy();
        symcopy->index = (*ii)->index;
        symcopy->owner = this;
        (*ii) = symcopy;
      }

      return (*ii);
    }

  return NULL;
}

TSymbol *SymbolTable::getSymbol(int index)
{
  int idx=0;

  if (table.empty())
    return NULL;

  TI ii;
  for (ii=table.begin(); ii != table.end() && idx != index+start; ++ii, ++idx);

  if (ii != table.end())
  {
    if ((*ii)->owner != this)
    {
      LOG(plog.crawl, HDR << this << " is not owner of symbol " << (*ii) << ", reposessing" << std::endl);
      TSymbol *symcopy = (*ii)->copy();
      symcopy->index = (*ii)->index;
      symcopy->owner = this;
      (*ii) = symcopy;
    }

    return (*ii);
  }

  return NULL;
}

void SymbolTable::addReturnVariable(Type *type)
{
  Variable *var = new Variable("__return_variable__", new CodeValue(getRandomId(type)));
  addSymbol(var);
}

Id *SymbolTable::findReturnVariable()
{
  Variable *var = dynamic_cast<Variable*>(findSymbol("__return_variable__"));

  if (var)
  {
    CodeValue *cv = dynamic_cast<CodeValue*>(var->data);
    if (!cv)
      error("Return variable is non-Code");

    Id *id = dynamic_cast<Id*>(cv->data);
    if (!id)
      error("Return variable's data is non-Id");

    return id->copy();
  }
  else
    return new Id(&BasicVoid, "__unknown_return_variable__");
}

void SymbolTable::popReturnVariable()
{
  Variable *var = dynamic_cast<Variable*>(findSymbol("__return_variable__"));

  if (var)
    var->identifier = "__popped_return_variable__";
}

std::string SymbolTable::print()
{
  std::ostringstream out;

  for (TI ii=table.begin(); ii != table.end(); ++ii)
    out << (*ii)->toString() << std::endl;

  return out.str();
}

void SymbolTable::clear()
{
  while (!table.empty())
  {
    if (table.back()->owner == this && typeid(*table.back()) != typeid(TypeSymbol))
      delete table.back();
    table.pop_back();
  }

  start = 0;

  newFrame();
}

void SymbolTable::dirtyChanged(SymbolTable *rhs, bool mergeAssignments)
{
//  plog.residualize() << "Dirtying\n" << print() << "from\n" << rhs->print();

  TI ii=table.begin(), jj=rhs->table.begin();

  // Allow for extra symbols because of "promoted declarations"
  for (int i=0; i < abs(start-rhs->start); i++)
    if (start < rhs->start)
      jj++;
    else
      ii++;

  for (; ii != table.end() && jj != rhs->table.end(); ++ii, ++jj)
  {
    if ((*ii)->identifier != (*jj)->identifier)
    {
      plog.err() << HDR << "Merging incompatible symbol tables" << std::endl;
      plog.err() << "table 1" << std::endl;
      plog.err() << "" << this->print();
      plog.err() << "table 2" << std::endl;
      plog.err() << "" << rhs->print();

      exit(-1);

    }

    Variable *var  = dynamic_cast<Variable*>(*ii),
             *var2 = dynamic_cast<Variable*>(*jj);

//    LOG(plog.crawl, HDR << (*ii)->identifier << " from " << (*jj)->identifier
//		 << std::endl);

    if (var && var2)
    {
      if ((*ii)->owner != this)
      {
        LOG(plog.crawl, HDR << this << " is not owner of symbol " << (*ii) << ", reposessing" << std::endl);
        TSymbol *symcopy = (*ii)->copy();
        symcopy->index = (*ii)->index;
        symcopy->owner = this;
        (*ii) = symcopy;
        var = dynamic_cast<Variable*>(*ii);
      }

      if ((*jj)->owner != rhs)
      {
        LOG(plog.crawl, HDR << rhs << " is not owner of symbol " << (*jj) << ", reposessing" << std::endl);
        TSymbol *symcopy = (*jj)->copy();
        symcopy->index = (*jj)->index;
        symcopy->owner = rhs;
        (*jj) = symcopy;
        var2 = dynamic_cast<Variable*>(*jj);
      }

      var->data->dirtyChanged(this, var2->data, mergeAssignments);
    }
    else
      if (var || var2)
	error("Merging incompatiable symbol tables");
  }

  if (ii != table.end() || jj!=rhs->table.end())
    LOG(plog.symbols, HDR << "Merging symbol tables of different lengths" << std::endl);
}

void SymbolTable::removeGenerators(void)
{
  for (TI ii=table.begin(); ii != table.end(); ++ii)
  {
    Variable *var  = dynamic_cast<Variable*>(*ii);

    if (var)
    {
      if ((*ii)->owner != this)
      {
        LOG(plog.crawl, HDR << this << " is not owner of symbol " << (*ii) << ", reposessing" << std::endl);
        TSymbol *symcopy = (*ii)->copy();
        symcopy->index = (*ii)->index;
        symcopy->owner = this;
        (*ii) = symcopy;
        var = dynamic_cast<Variable*>(*ii);
      }

      var->data->removeGenerators();
    }
  }
}

void SymbolTable::residualizeGenerators(void)
{
  for (TI ii=table.begin(); ii != table.end(); ++ii)
  {
    Variable *var  = dynamic_cast<Variable*>(*ii);

    if (var)
    {
      if ((*ii)->owner != this)
      {
        LOG(plog.crawl, HDR << this << " is not owner of symbol " << (*ii) << ", reposessing" << std::endl);
        TSymbol *symcopy = (*ii)->copy();
        symcopy->index = (*ii)->index;
        symcopy->owner = this;
        (*ii) = symcopy;
        var = dynamic_cast<Variable*>(*ii);
      }

      var->data->residualize(this);
    }
  }
}

std::string SymbolTable::symbolToString(std::string identifier)
{
  TSymbol *sym = findSymbol(identifier);
  CStr res;

  if (sym)
    return sym->toString();
  else
    return (char*)(CStr("unknown symbol ") << identifier);
}

std::string SymbolTable::printKnownValues()
{
  std::ostringstream sout;
  Compound *out = new Compound(true);

  for (TI ii=table.begin(); ii != table.end(); ++ii)
  {
    Variable *var = dynamic_cast<Variable*>(*ii);

    if (var)
    {
      Id *id = new Id(&Unknown, var->identifier);
      var->data->outputKnownValues(id, out);
      delete id;
    }
  }

  sout << out->toString();
  delete out;

  return sout.str();  
}
